{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "MultinomialHMM has undergone major changes. The previous version was implementing a CategoricalHMM (a special case of MultinomialHMM). This new implementation follows the standard definition for a Multinomial distribution (e.g. as in https://en.wikipedia.org/wiki/Multinomial_distribution). See these issues for details:\n",
      "https://github.com/hmmlearn/hmmlearn/issues/335\n",
      "https://github.com/hmmlearn/hmmlearn/issues/340\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "from hmmlearn import hmm\n",
    "\n",
    "states = [\"box 1\", \"box 2\", \"box3\"]\n",
    "n_states = len(states)\n",
    "\n",
    "observations = [\"red\", \"white\"]\n",
    "n_observations = len(observations)\n",
    "\n",
    "start_probability = np.array([0.2, 0.4, 0.4])\n",
    "\n",
    "transition_probability = np.array([\n",
    "  [0.5, 0.2, 0.3],\n",
    "  [0.3, 0.5, 0.2],\n",
    "  [0.2, 0.3, 0.5]\n",
    "])\n",
    "\n",
    "emission_probability = np.array([\n",
    "  [0.5, 0.5],\n",
    "  [0.4, 0.6],\n",
    "  [0.7, 0.3]\n",
    "])\n",
    "\n",
    "model = hmm.MultinomialHMM(n_components=n_states, n_iter=20, tol=0.001)\n",
    "model.startprob_=start_probability\n",
    "model.transmat_=transition_probability\n",
    "model.emissionprob_=emission_probability\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "n_trials must be set",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[4], line 2\u001b[0m\n\u001b[0;32m      1\u001b[0m seen \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39marray([[\u001b[38;5;241m0\u001b[39m,\u001b[38;5;241m1\u001b[39m,\u001b[38;5;241m0\u001b[39m]])\u001b[38;5;241m.\u001b[39mT\n\u001b[1;32m----> 2\u001b[0m logprob, box \u001b[38;5;241m=\u001b[39m \u001b[43mmodel\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdecode\u001b[49m\u001b[43m(\u001b[49m\u001b[43mseen\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43malgorithm\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mviterbi\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[0;32m      3\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThe ball picked:\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m, \u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mjoin(\u001b[38;5;28mmap\u001b[39m(\u001b[38;5;28;01mlambda\u001b[39;00m x: observations[x], seen)))\n\u001b[0;32m      4\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThe hidden box\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m, \u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mjoin(\u001b[38;5;28mmap\u001b[39m(\u001b[38;5;28;01mlambda\u001b[39;00m x: states[x], box)))\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python39\\site-packages\\hmmlearn\\base.py:336\u001b[0m, in \u001b[0;36m_AbstractHMM.decode\u001b[1;34m(self, X, lengths, algorithm)\u001b[0m\n\u001b[0;32m    301\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m    302\u001b[0m \u001b[38;5;124;03mFind most likely state sequence corresponding to ``X``.\u001b[39;00m\n\u001b[0;32m    303\u001b[0m \n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m    333\u001b[0m \u001b[38;5;124;03mscore : Compute the log probability under the model.\u001b[39;00m\n\u001b[0;32m    334\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m    335\u001b[0m check_is_fitted(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mstartprob_\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m--> 336\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_check\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    338\u001b[0m algorithm \u001b[38;5;241m=\u001b[39m algorithm \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39malgorithm\n\u001b[0;32m    339\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m algorithm \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m DECODER_ALGORITHMS:\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python39\\site-packages\\hmmlearn\\hmm.py:936\u001b[0m, in \u001b[0;36mMultinomialHMM._check\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m    934\u001b[0m     \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mn_features \u001b[38;5;241m=\u001b[39m n_features\n\u001b[0;32m    935\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mn_trials \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m--> 936\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mn_trials must be set\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
      "\u001b[1;31mValueError\u001b[0m: n_trials must be set"
     ]
    }
   ],
   "source": [
    "seen = np.array([[0,1,0]]).T\n",
    "logprob, box = model.decode(seen, algorithm=\"viterbi\")\n",
    "print(\"The ball picked:\", \", \".join(map(lambda x: observations[x], seen)))\n",
    "print(\"The hidden box\", \", \".join(map(lambda x: states[x], box)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "MultinomialHMM has undergone major changes. The previous version was implementing a CategoricalHMM (a special case of MultinomialHMM). This new implementation follows the standard definition for a Multinomial distribution (e.g. as in https://en.wikipedia.org/wiki/Multinomial_distribution). See these issues for details:\n",
      "https://github.com/hmmlearn/hmmlearn/issues/335\n",
      "https://github.com/hmmlearn/hmmlearn/issues/340\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0.01366745 0.00944414 0.97688841]\n",
      "[[6.08459405e-01 2.32638495e-01 1.58902100e-01]\n",
      " [5.44961396e-01 5.00664705e-04 4.54537939e-01]\n",
      " [1.99743220e-01 7.81420501e-01 1.88362787e-02]]\n",
      "[[1.]\n",
      " [1.]\n",
      " [1.]]\n",
      "-1.1102230246251565e-15\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "from hmmlearn import hmm\n",
    "\n",
    "states = [\"box 1\", \"box 2\", \"box3\"]\n",
    "n_states = len(states)\n",
    "\n",
    "observations = [\"red\", \"white\"]\n",
    "n_observations = len(observations)\n",
    "model = hmm.MultinomialHMM(n_components=n_states, n_iter=20, tol=0.01)\n",
    "\n",
    "D1 = [[1], [0], [0], [0], [1], [1], [1]]\n",
    "D2 = [[1], [0], [0], [0], [1], [1], [1], [0], [1], [1]]\n",
    "D3 = [[1], [0], [0]]\n",
    "\n",
    "X = np.concatenate([D1, D2, D3])\n",
    "\n",
    "model.fit(X)\n",
    "print (model.startprob_)\n",
    "print (model.transmat_)\n",
    "print (model.emissionprob_)\n",
    "print (model.score(X))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "MultinomialHMM has undergone major changes. The previous version was implementing a CategoricalHMM (a special case of MultinomialHMM). This new implementation follows the standard definition for a Multinomial distribution (e.g. as in https://en.wikipedia.org/wiki/Multinomial_distribution). See these issues for details:\n",
      "https://github.com/hmmlearn/hmmlearn/issues/335\n",
      "https://github.com/hmmlearn/hmmlearn/issues/340\n",
      "MultinomialHMM has undergone major changes. The previous version was implementing a CategoricalHMM (a special case of MultinomialHMM). This new implementation follows the standard definition for a Multinomial distribution (e.g. as in https://en.wikipedia.org/wiki/Multinomial_distribution). See these issues for details:\n",
      "https://github.com/hmmlearn/hmmlearn/issues/335\n",
      "https://github.com/hmmlearn/hmmlearn/issues/340\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n",
      "Topics discussed:\n",
      "['cat', 'cat', 'cat', 'cat', 'cat', 'dog', 'dog', 'cat', 'cat', 'dog', 'cat', 'cat', 'cat', 'cat', 'cat', 'dog', 'dog', 'cat', 'cat', 'dog', 'cat', 'cat', 'cat', 'cat', 'cat', 'dog', 'dog', 'cat', 'cat', 'dog', 'cat', 'cat', 'cat', 'cat', 'cat', 'dog', 'dog', 'cat', 'cat', 'dog', 'cat', 'cat', 'cat', 'cat', 'cat', 'dog', 'dog', 'cat', 'cat', 'dog']\n",
      "Learned emission probs:\n",
      "[[2.57129200e-01 2.86190571e-02 4.28541642e-01 2.85710101e-01]\n",
      " [1.33352852e-01 7.33292496e-01 2.67548571e-05 1.33327897e-01]]\n",
      "Learned transition matrix:\n",
      "[[0.71429762 0.28570238]\n",
      " [0.50007593 0.49992407]]\n",
      "\n",
      "New Model\n",
      "Topics discussed:\n",
      "['dog', 'dog', 'dog', 'dog', 'dog', 'cat', 'cat', 'dog', 'dog', 'cat', 'dog', 'dog', 'dog', 'dog', 'dog', 'cat', 'cat', 'dog', 'dog', 'cat', 'dog', 'dog', 'dog', 'dog', 'dog', 'cat', 'cat', 'dog', 'dog', 'cat', 'dog', 'dog', 'dog', 'dog', 'dog', 'cat', 'cat', 'dog', 'dog', 'cat', 'dog', 'dog', 'dog', 'dog', 'dog', 'cat', 'cat', 'dog', 'dog', 'cat']\n",
      "Learned emission probs:\n",
      "[[1.33318133e-01 7.33363713e-01 3.15980986e-07 1.33317838e-01]\n",
      " [2.57137017e-01 2.86287250e-02 4.28528536e-01 2.85705722e-01]]\n",
      "Learned transition matrix:\n",
      "[[0.49982552 0.50017448]\n",
      " [0.28568572 0.71431428]]\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "from hmmlearn import hmm\n",
    "\n",
    "# For this example, we will model the stages of a conversation,\n",
    "# where each sentence is \"generated\" with an underlying topic, \"cat\" or \"dog\"\n",
    "states = [\"cat\", \"dog\"]\n",
    "id2topic = dict(zip(range(len(states)), states))\n",
    "# we are more likely to talk about cats first\n",
    "start_probs = np.array([0.6, 0.4])\n",
    "\n",
    "# For each topic, the probability of saying certain words can be modeled by\n",
    "# a distribution over vocabulary associated with the categories\n",
    "\n",
    "vocabulary = [\"tail\", \"fetch\", \"mouse\", \"food\"]\n",
    "# if the topic is \"cat\", we are more likely to talk about \"mouse\"\n",
    "# if the topic is \"dog\", we are more likely to talk about \"fetch\"\n",
    "emission_probs = np.array([[0.25, 0.1, 0.4, 0.25],\n",
    "                           [0.2, 0.5, 0.1, 0.2]])\n",
    "\n",
    "# Also assume it's more likely to stay in a state than transition to the other\n",
    "trans_mat = np.array([[0.8, 0.2], [0.2, 0.8]])\n",
    "\n",
    "\n",
    "# Pretend that every sentence we speak only has a total of 5 words,\n",
    "# i.e. we independently utter a word from the vocabulary 5 times per sentence\n",
    "# we observe the following bag of words (BoW) for 8 sentences:\n",
    "observations = [[\"tail\", \"mouse\", \"mouse\", \"food\", \"mouse\"],\n",
    "        [\"food\", \"mouse\", \"mouse\", \"food\", \"mouse\"],\n",
    "        [\"tail\", \"mouse\", \"mouse\", \"tail\", \"mouse\"],\n",
    "        [\"food\", \"mouse\", \"food\", \"food\", \"tail\"],\n",
    "        [\"tail\", \"fetch\", \"mouse\", \"food\", \"tail\"],\n",
    "        [\"tail\", \"fetch\", \"fetch\", \"food\", \"fetch\"],\n",
    "        [\"fetch\", \"fetch\", \"fetch\", \"food\", \"tail\"],\n",
    "        [\"food\", \"mouse\", \"food\", \"food\", \"tail\"],\n",
    "        [\"tail\", \"mouse\", \"mouse\", \"tail\", \"mouse\"],\n",
    "        [\"fetch\", \"fetch\", \"fetch\", \"fetch\", \"fetch\"]]\n",
    "\n",
    "# Convert \"sentences\" to numbers:\n",
    "vocab2id = dict(zip(vocabulary, range(len(vocabulary))))\n",
    "def sentence2counts(sentence):\n",
    "    ans = []\n",
    "    for word, idx in vocab2id.items():\n",
    "        count = sentence.count(word)\n",
    "        ans.append(count)\n",
    "    return ans\n",
    "\n",
    "X = []\n",
    "for sentence in observations:\n",
    "    row = sentence2counts(sentence)\n",
    "    X.append(row)\n",
    "\n",
    "data = np.array(X, dtype=int)\n",
    "\n",
    "# pretend this is repeated, so we have more data to learn from:\n",
    "lengths = [len(X)]*5\n",
    "sequences = np.tile(data, (5,1))\n",
    "\n",
    "\n",
    "# Set up model:\n",
    "model = hmm.MultinomialHMM(n_components=len(states),\n",
    "        n_trials=len(observations[0]),\n",
    "        n_iter=50,\n",
    "        init_params='')\n",
    "\n",
    "model.n_features = len(vocabulary)\n",
    "model.startprob_ = start_probs\n",
    "model.transmat_ = trans_mat\n",
    "model.emissionprob_ = emission_probs\n",
    "model.fit(sequences, lengths)\n",
    "logprob, received = model.decode(sequences)\n",
    "\n",
    "print(\"Topics discussed:\")\n",
    "print([id2topic[x] for x in received])\n",
    "\n",
    "print(\"Learned emission probs:\")\n",
    "print(model.emissionprob_)\n",
    "\n",
    "print(\"Learned transition matrix:\")\n",
    "print(model.transmat_)\n",
    "\n",
    "# Try to reset and refit:\n",
    "new_model = hmm.MultinomialHMM(n_components=len(states),\n",
    "        n_trials=len(observations[0]),\n",
    "        n_iter=50, init_params='ste')\n",
    "\n",
    "new_model.fit(sequences, lengths)\n",
    "logprob, received = new_model.decode(sequences)\n",
    "\n",
    "print(\"\\nNew Model\")\n",
    "print(\"Topics discussed:\")\n",
    "print([id2topic[x] for x in received])\n",
    "\n",
    "print(\"Learned emission probs:\")\n",
    "print(new_model.emissionprob_)\n",
    "\n",
    "print(\"Learned transition matrix:\")\n",
    "print(new_model.transmat_)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "myenv2",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.18"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
